﻿

namespace OfficeClip.OpenSource.StyleFix.Drivers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;

    public class SpacingRules : StyleCopRules
    {
        internal void SA1000_KeywordsMustBeSpacedCorrectly(ref List<SFWorkingLine> workingLines)
        {
            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1000"))
                {
                    workingLine.Line = Regex.Replace(
                        workingLine.Line,
                        @"(new|switch|foreach|for|catch|fixed|from|group|if|in|into|join|let|lock|orderby|return|select|stackalloc|throw|using|where|while|yield)(\S)",
                        @"$1 $2");
                    workingLine.Line = Regex.Replace(
                        workingLine.Line,
                        @"(checked|default|sizeof|typeof|unchecked)\s+(\S)",
                        @"$1$2");

                    workingLine.Line = Regex.Replace(
                        workingLine.Line,
                        @"new\s\[",
                        @"new[");
                }
            }
        }

        internal void SA1001_CommasMustBeSpaceCorrectly(ref List<SFWorkingLine> workingLines)
        {
            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1001"))
                {
                    workingLine.Line = Regex.Replace(workingLine.Line, @"(\S)\s+,\s*", @"$1, ");
                    workingLine.Line = Regex.Replace(workingLine.Line, @",(\S)", @", $1");
                }
            }
        }

        internal void SA1002_SemicolonsMustBeSpaceCorrectly(ref List<SFWorkingLine> workingLines)
        {
            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1002"))
                {
                    workingLine.Line = Regex.Replace(workingLine.Line, @"(\S)\s+;\s*", @"$1; ");
                    workingLine.Line = Regex.Replace(workingLine.Line, @";(\S)", @"; $1");
                }
            }
        }

        internal void SA1003_SymbolsMustBeSpaceCorrectly(ref List<SFWorkingLine> workingLines)
        {
            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1003"))
                {
                    // operator must be surrounded by white space on both sides
                    workingLine.Line = Regex.Replace(
                        workingLine.Line,
                        @"(\w)(=|<|>|\+|\*|-|%|<<|>>|<=|>=|==|!=|&|&&|\||\|\||\?:|\+=|-=|\*=|/=|%=|\|=|^=|<<=|>>=|\?\?)\s*",
                        @"$1 $2 ");
                    // unary operator should not be followed by a space but must be preceded by a space
                    // TODO
                }
            }
        }

        internal void SA1004_DocumentationLinesMustBeginWithSingleSpace(ref List<SFWorkingLine> workingLines)
        {
            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1004"))
                {
                    workingLine.Line = Regex.Replace(workingLine.Line, @"///\s*(.)", @"/// $1");
                }
            }
        }

        internal void SA1005_SingleLineCommentsMustBeginWithSingleSpace(ref List<SFWorkingLine> workingLines)
        {
            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1005"))
                {
                    workingLine.Line = Regex.Replace(workingLine.Line, @"//\s*(\S)", @"// $1", RegexOptions.None);
                }
            }
        }

        internal void SA1009_ClosingParenthesisMustBeSpacedCorrectly(ref List<SFWorkingLine> workingLines)
        {
            int lineOffset = 0;
            for(int i = 0; i < workingLines.Count; i++)
            {
                SFWorkingLine workingLine = workingLines[i];
                if (IsLineViolated(workingLine, "SA1009"))
                {
                    workingLine.LineNo -= lineOffset;

                    // Case 1: The closing parenthesis should not be preceded by a whitespace and the
                    // closing parenthesis is not by itself on the line
                    workingLine.Line = Regex.Replace(workingLine.Line, @"(\S)\s+\)", @"$1)", RegexOptions.None);
                    ////workingLine.Line.Replace(" )", ")");
                    
                    // Case 2: The closing parenthesis should not be followed by a whitespace if there is a ; ( ) [ ] ; ,
                    workingLine.Line = Regex.Replace(workingLine.Line, @"\)\s+(\(|\)|\[|\]|;|,)", @")$1", RegexOptions.None);
                    
                    // Case 3: The closing parenthesis must not be followed by a whitespace after a cast
                    workingLine.Line = Regex.Replace(workingLine.Line, @"(\(\w+\))\s+(\S)", @"$1$2", RegexOptions.None);

                    // Case 4: Line handling
                    if (Regex.IsMatch(workingLine.Line, @"\s+\)"))
                    {
                        int lastline = i - 1;
                        while (lastline > 0 && string.IsNullOrEmpty(workingLines[lastline].Line.Trim()))
                        {
                            workingLines.RemoveAt(lastline);
                            lineOffset++;
                        }
                        if (workingLines[lastline].Line.Contains("//"))
                        {
                            workingLine.Line = workingLine.Line.TrimStart();
                        }
                        else
                        {
                            workingLines[lastline].Line = workingLines[lastline].Line.TrimEnd() + workingLine.Line.TrimStart();
                            workingLines.Remove(workingLine);
                            lineOffset++;
                            i--;
                        }
                    }
                }
            }
        }

        internal void SA1010_OpeningSquareBracketsMustBeSpacedCorrectly(ref List<SFWorkingLine> workingLines)
        {
            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1010"))
                {
                    // preceded by whitespace
                    workingLine.Line = Regex.Replace(workingLine.Line, @"(\S)\s+\[", @"$1[");
                    // followed by whitespace
                    workingLine.Line = Regex.Replace(workingLine.Line, @"\[\s+(\S)", @"[$1");
                }
            }
        }

        internal void SA1012_OpeningCurlyBracketsMustBeSpacedCorrectly(ref List<SFWorkingLine> workingLines)
        {
            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1012"))
                {
                    // preceded by whitespace
                    workingLine.Line = Regex.Replace(workingLine.Line, @"(\S)\{", @"$1 {");
                    // followed by whitespace
                    workingLine.Line = Regex.Replace(workingLine.Line, @"\{(\S)", @"{ $1");
                }
            }
        }

        internal void SA1013_ClosingCurlyBracketsMustBeSpacedCorrectly(ref List<SFWorkingLine> workingLines)
        {
            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1013"))
                {
                    // preceded by whitespace
                    workingLine.Line = Regex.Replace(workingLine.Line, @"(\S)\}", @"$1 }");
                    // followed by whitespace
                    workingLine.Line = Regex.Replace(workingLine.Line, @"\}([^,);\s])", @"} $1");
                }
            }
        }

        internal void SA1027_TabsMustNotBeUsed(ref List<SFWorkingLine> workingLines)
        {
            foreach (SFWorkingLine workingLine in workingLines)
            {
                if (IsLineViolated(workingLine, "SA1027"))
                {
                    workingLine.Line = workingLine.Line.Replace("\t", "    ");
                }
            }
        }
    }
}
